Alrighty.
Welcome back.
In this lecture we're going to start scripting the main menu for our game.
Specifically, we want to be able to press the play button and spawn into the map.
When the game is in the intermission stage, the plan is to have the game be in different stages.
For example, if a wave is in progress, that would be one stage.
If a wave is over and we're waiting for the next wave, that would be like the intermission stage and
so on.
So we only want players to be able to join the game during an intermission stage.
Now, an easy way for us to know what stage the game is in would be for the server to set an attribute
on the workspace, you could call it something like Game state or game stage or whatever, and using
that attribute, we would check if it was appropriate for us to spawn on to the map.
We also want to be able to display this frame right here on our screen when the player dies, and it
will display a message of like you died.
So this frame is very simple.
It's just a black frame with a text label inside of it, and we want to be able to fade it onto the
screen to completely fade out the screen like that, and then display this message right here.
If I change the text transparency to zero, that says you died.
So that's another thing we're going to want to add to our UI.
So let's get started.
We're going to create a new local script inside of our starting menu.
And I'm going to call this my starting menu handler.
Now inside of here I'm going to paste in a comment template that I have attached to this lecture.
That way it's easier for us to organize our scripts.
So we're going to have a section where we can store our services, our variables, any private functions
we have.
I'm going to get rid of this section because we're not going to need it.
Any event handlers we have, and then any main functionality that we need to execute in our script.
So for our starting menu handler, we want to first grab our starting menu.
That's a given.
And the easiest way to do that is that since our local script is a child of our starting menu, we can
literally just refer to the script itself and then get the parent of our script, which would be the
menu.
So we could create a variable and we could call it our starting menu.
And that's equal to script dot parent.
Then we can go ahead and create variables for all of the other important stuff in our menu.
For example, we can go ahead and get um, some of the frames that we're going to be using later on
in the course, for example, the spectate frame.
And it's going to be equal to our starting menu dot spectate frame.
We can go ahead and grab the shop frame that's equal to the starting menu dot store frame.
And then we can go ahead and grab um, that background or that main image where the blur is and where
all of our play buttons are in.
And we'll just call that blur image and that's equal to starting menu dot image label.
Then we can go ahead and grab those buttons that are inside of our background image.
One is our play button, so that's equal to blur image dot play.
We can grab our shot button that's equal to blur image dot shop.
Or I believe it's store.
Yeah, store.
And then we can grab our spectate.
Button and that's equal to blur image dot.
Spectate.
So now what we want to do is inside of our handler section, we want to go ahead and listen for when
our play button is clicked or touched.
So we can refer to our play button and there's an event in there.
Usually some people would use mouse button one click, but this event is only for people who are on
a computer because it's going to listen for when a mouse clicks it.
But if you want to be able to listen to all sorts of inputs, including from mobile users or even console
users, then the best event to use for listening to input for our button is an event called activated.
So it says fires when the button is activated.
And this works for PC users, mobile users, and console users.
Then we can go ahead and connect a Lambda function to this.
So we'll connect our function.
And when the player presses the play button, we want to make sure that the game is in the state that
allows for us to, you know, join the game.
So we would set that, uh, attribute on the workspace of game state.
And then here we could check if that game state is equal to an intermission stage.
And if it is, then we could tell the server, hey, can you respawn us and put us on the map?
So that means we're also going to need to create a server script that's going to set the state of the
game on the workspace.
So another thing we could do here is we could create a new script inside of a service script service.
This is going to be a server script.
And I'm going to call this my game handler.
And I'm also going to paste in that exact same template that we just pasted in previously.
I'm going to get rid of that and I'm going to get rid of that.
And inside of here, what we could do in the main section is we would set an attribute on the workspace.
We could call it game state, and we could set it equal to something like a string.
That represents what the current state of the game is like intermission or something like that.
Now we could just use just some, uh, magic values that are just sitting here, and a magic value means
that it's just a value that I've typed up right here.
But if we want to be able to, like, control what the different values we should be comparing represents,
we could create a custom enum using a module script.
And inside of that module script, we could have a set of predefined values that represent each of the
different stages that our game could be in.
So we know that in enum stores different things.
For example, in the material section it defines different constants to represent different things.
Like for materials there's different constants to represent like a neon part or a smooth plastic part
or a concrete part or whatever.
So we could do the exact same thing with a module script.
So inside of replicated storage and in our modules folder and inside of our enums folder, what we could
do is we could create a new module script.
And we can go ahead and call this our game state enum.
So this module script is basically going to act as an enum for us.
And inside of here, what we can do is simply return a table that contains those defined constants.
So for example, one of the states that our game could be in would be the start intermission.
And we would just set this to, uh, the same string of start intermission.
So the start intermission would be like when all of the players join a new server, when a new server
is created.
Obviously there wasn't a previous wave that occurred.
So we're basically in the beginning intermission or the starting intermission where we're waiting for
a player to join into the game.
And then once a player actually does join the game during the start intermission, then we could swap
it to a regular intermission.
And during this regular intermission is where we would have a countdown until a wave begins.
So that countdown could be like 60s.
And then after those 60s are up, then we could change the stage of our game to be a wave that is in
progress, so we could create something like wave in progress, and again, just set it equal to a string.
That is itself because we want to make this easy to access the values in.
So that's why we're setting it up as a dictionary that has a key and a value.
It doesn't really matter what these values are, it just matters that these values are unique from each
other, because we want these values to represent a different, uh, stages or states that our game
is in.
So we can have a start intermission, a regular intermission.
We can have a state where a wave is in progress.
We can have another state where the game is over.
And this one could represent when all of the players like lose.
So all the players die, we would be in a game over.
But if all the players win, then we could have a different, um, state for that.
We could call it game Over win.
And then the last state we could have is like a state where the server cleans up everything after a
game is over.
So we could call this state like waiting.
And while we're in the waiting state, it'll clean everything up and then set it back to the start intermission.
And then we'll start the loop over again.
So it starts as a start, intermission.
Then we go into an intermission.
Then we go into a wave.
If we complete the wave, then we go back to intermission and then back to wave until we complete all
the waves.
If the players complete all of the waves, then it's a game over and they've won.
But if all the players die, then it's a game over and they've lost.
And then once a game over occurs, then we're going to clean up the game.
And then after we're done cleaning up the game, we're going to put it back into a start intermission.
So this module script is going to store these defined constants to tell us what state our game is going
to be in.
So we can go ahead and grab this module script from both our server script and our local script, because
it's inside of replicated storage.
So we can go ahead and grab replicated storage using game git service function.
Replicated storage.
And then in my variable section I'll create a variable.
I'll call it my game state enum.
And that's going to be equal to.
We'll use the require function to require our module script.
And that's in replicated storage dot modules dot enums.
And we'll get our game state enum.
And now using our game state enum we can go ahead and set an attribute on the workspace to be for example
the start intermission.
So now the workspace has this string of start intermissions set on it, and then the client can check
what string is set on the workspace.
Compare it to the values inside of our module script to make a determination of whether or not we are
able to spawn into the game or not.
So when we activate this button, what we can do is we can check if the workspace git attribute and
the attribute is game state.
And we can check if this attribute is equal to the game states that are defined in our module script.
So let's go ahead and get that game state enum in here as well.
We'll create a variable game state enum.
And then we'll also grab a replicated storage in here as well.
So replicated storage is equal to game Getservice replicated storage.
And I'm just going to copy what we did inside of our server script and then just paste that here.
So now we have the game state enum on the client.
Then we can go down and we can check if the current game state is equal to the game state enum of,
let's say the start intermission, or if our game state is equal to the regular intermission.
If it is, then that means we are able to spawn.
It's okay to spawn.
Otherwise, we know here that it's not okay to spawn, and we need to let the player know that a game
is currently in progress.
However, if it is okay to spawn, then what I want to do is I want to fade the player screen to black
and then once the player screen is black, we can go ahead and tell the server, hey, respawn us.
The server respawns us onto the map and then we can fade.
Um, that black screen off of our screen again.
So what we can do is I'm going to create a dedicated function for fading UI elements on my screen.
I'm going to call it fade.
And to this function we're going to pass a UI element as well as um, the goal that we would like to
fade it to using the tween service.
So we're also going to grab the tween service.
Sequel to game jet.
Service between service.
And inside of here.
What I want to do is I want to create a tween.
Using twin service create, and that's going to be on the UI element passed to this function.
So UI element.
The tween info, we can just do a default of like 0.5 seconds.
We don't want to wait too long for whatever this UI element is to fade on our screen, and then the
goal is simply going to be the goal passed to our function.
And now we're just going to play that tween.
So now what we could do is we could call our fade function, and something that we want to fade onto
our screen is going to be, for example, our black frame right here.
So we could refer to our starting menu and pass our black frame.
And the goal for that is that we want to update the background transparency to be completely opaque
or set to the value of zero.
So background transparency we want to set equal to zero.
So now it's going to fade on to our screen.
And we're going to wait for one second just to make sure that it's completely faded onto our screen.
And after we have waited for one second, we can go ahead and tell the server right here, hey, please
respawn us.
So that means we're going to have to use a remote event to fire or communicate to the server that okay,
my screen has been faded to black, please respawn me.
So what we can do is inside of replicated storage and in our remotes folder and inside of the GUI folder,
I have a remote event right here called respawn.
So let's go ahead and make a reference to that.
So I'll create a variable, I'll call it my respawn event and that's equal to replicated storage dot
remotes dot g y dot respawn.
And then I can copy this and do the exact same thing for my server script.
Because my server script also needs to listen to this event.
And then down here I can just get my respawn event and then I can fire to the server that I would like
to be respawned.
After we tell the server we would like to be respawn, I'm going to wait for another second, and then
after that second, we're going to go ahead and fade out that black frame on our screen.
And we also want to hide the we want to hide this menu off of our screen once we have spawned into the
map.
So that's very easy to do.
We just refer to our blur image which contains all those buttons, and we're going to set the visibility
equal to false.
So we don't see it on our screen anymore.
And then we're going to fade our starting menu back to be transparent again.
So we're going to set the background transparency equal to one.
Now going back to our game handler server script.
What we want to do here is we want to go ahead and listen to when our respawn event gets fired.
So we're going to listen to the on server event and connect a function to this event.
And we're going to get the player that fired this event.
And here we also need to make sure that the workspace has the attribute set on it.
And it's set to a state where we can go ahead and respawn the player.
So if the workspace get attribute game state, if it's not equal to the game state.
Enum of start.
Intermission.
And it's not equal to the regular intermission.
So workspace get attribute.
Game state if it's not equal to the game state.
Enum dot intermission.
Then we're just going to return out of this function because we don't want to respond to player.
The state of our game is in a state where we're not allowed to respond players.
Otherwise, if we are in a state where we can respond to player, then what we want to do is we want
to set the player to the alive team.
And then when we set the player to the alive team, then we can reload their character, which is going
to spawn them on the map.
So let's go ahead and grab the team service.
I'll just call it teams and it's equal to game get service teams.
And here is where we would update the players team property equal to.
And we're going to get the alive team.
Afterwards.
We can call the function on our player load character to create a new character for the player, removing
the old one.
So just like that, now when the player fires that event to us and the game is in a state where we can
respawn them on the map, we're going to update their team to be alive and we're going to load their
character.
Now, another important thing we need to do is we want to make sure that when the player dies, that
we set their team equal to the dead team.
So another thing we want to listen to is we want to listen to the player added event to listen for when
a player joins our game and we want to listen for when that player's character dies.
So let's go ahead and get the player service.
And here we can listen to the players player at an event.
And we can grab that player from the event.
And then we want to listen for when that player has a character added to them.
And we'll connect a function and get that character.
And then inside of that character we want to listen to when their humanoid dies.
So the humanoid has an event called died.
And we can connect a function to that.
And this will let us know when, um, a specific player's character dies.
When a player's character dies, then we want to update their team equal to teams.
Dot dead.
And then by default, we also want to make sure that when a player joins our game, that their team
is set to the dead team by default.
Okay, so now we should be able to test our play button that when we click it, we get spawned into
the map.
And that's because the server is going to set the game state equal to the start intermission, which
is a state that we're allowed to spawn in.
So when we click our button, our screen should fade to black.
The server should respawn us.
The buttons and the text of this stuff should disappear off of our screen, and then we should fade
that black frame off of our screen again.
So let's go ahead and try it out.
We'll head to the test tab and hit play.
And here we are.
We're inside of the holding area for where all the dead players go.
But if we go ahead and click the play button there we go.
Our screen fades to black.
We were set to the alive team and the screen fades back out.
And as you can see now we are on the map.
Very cool.
Now the next thing we need to go ahead and do is we want to listen for when our player dies and when
they died.
We want to fade that black screen back onto the screen.
And then we want to display that text of you died.
Because if I reset, the server is going to set my team to the dead team, but my screen doesn't fade
out and give me that dead message.
And as you can see, I don't have access to that guy buttons anymore.
So I can't spectate players or I can't rejoin back into the game.
So we need to fix that.
So what we can do in our local script is we can go ahead and grab the player service.
And then we can make a variable for the local player.
So that's going to be equal to players dot local player to grab us.
And then I want to go ahead and listen to when my player has a character added, and we're basically
going to be doing the exact same thing that the server is doing.
We're going to listen for when my player dies and when my player dies.
Then I want to fade the screen to black, and then I want to re-enable the visibility of the the main
menu back on my screen.
So when the player gets a character added, we're going to get that character and then we're going to
get the character's humanoid.
Now, since when the character added event gets fired, all of the children of our character may not
be replicated right away.
So we're going to use the wait for child function just in case, and we're going to wait for our humanoid.
And then once we get it, we're going to get its died event and then connect a function to listen for
when that player dies.
Now, important thing to note is that there's a chance that our player's character is added before this
script executes, which means we may not be able to connect a function to the died event for our player's
character.
So we can add an if statement here to check whether or not the player already has a character.
So if they have a character already, then we want to go ahead and do the exact same thing that we did
here.
We're going to copy this, paste this there, but instead we want to replace this with player dot character.
So this just makes sure that if the script executes after a player's character is already added, we're
still listening to that character's died event.
And then we can go ahead and create a dedicated function for listening for when our player's character
dies, because we don't want to create two copies of our function in two different places.
Instead, we want both of these event connections to refer to the same function.
So we're going to call this function on character death.
And then we can go ahead and connect to this function.
So on character depth and make sure you're not calling it, but instead you're passing it to the connect
function.
So now we're listening for when the player dies.
And what do we want to do when the player dies?
Well, we want to go ahead and use our fade function, and we want to fade that starting menu dot black
frame back onto the screen.
So we're going to set the background transparency equal to zero.
And then we also want to fade in that you died message which is the text label in our starting menu.
So we can go ahead and fade in our starting menu dot black.
We'll get the text label in there.
And we want to go ahead and update its text transparency property and make that opaque as well.
And then after we do that, what we want to go ahead and do is we want to wait for when the player has
a character added.
So we're going to refer to the character added event, and we're going to yield or wait for this event
to fire.
Then once the player has a character added, then we can go ahead and re-enable the blur image visibility.
So that way we get the buttons back on our screen and then we can go ahead and fade that black menu
back out.
So I'll copy this, paste that here and we'll update the transparency values back to one.
So now if our player dies, we should fade this stuff back on our screen, and then we should fade it
back out once a player has a new character added.
So let's go ahead and try it out.
We'll playtest our game.
As you can see here we are in our holding box if we play the game.
There we go.
We are in the map.
And then if I die by resetting, as you can see, it says you died.
And then there we go.
It fades back out.
And now we have the main menu visible again on our screen.
Very cool.
And then of course, we can click it again and respawned back into the map and we can die again.
And it tells us that we've died.
Now, one more thing we want to listen for is what happens if we click the button, but we are not in
the start intermission or regular intermission?
Well, what we could do is we could update the text that is inside of our play button and we could tell
the player like, hey, please wait, there's a game in progress.
So what we could do is we could get our play button and set the text inside of it equal to be something
like game in progress.
Please wait.
And then we'll leave that display on the screen for like, um, we could wait for like three seconds.
And then after that we could set the text back to its original text.
So I'll create a variable, I'll call it original text.
And that's going to be equal to play button dot text.
So we're storing the text that was originally in our play button.
And then we can set play button text back equal to the original text.
So now if we go and playtest our game or actually let me go ahead and update the game state to be for
example, like wave in progress.
So that way we can't spawn into the map.
If I go and play test the game and then I hit play.
As you can see, game of progress, please wait.
And it doesn't let me respawn into the map.
Now there is a small problem with how we have this function set up here listening to our activated event.
And that's if.
What if we spam click this button.
If I spam, click this button.
As you can see, this text is remaining on our screen and it's not fading out.
And that's a problem.
And if I go and I were to set the game state back to, for example, the start intermission.
And then I went to go play test my game again.
And then I were to spam my button a bunch, so let me go ahead and spam it.
As you can see, we're getting a bunch of problems here.
We just got a whole bunch of errors in the console.
And that's because the server.
Had the event.
Where is it?
Had this event fire multiple times to reload the character over and over again and reset the player's
team back to the alive team.
And at the same time, we executed this chunk of code multiple times over and over and over again.
So what we want to do is we want to basically only execute everything in here once, and then once we're
done executing everything in here, then we can go ahead and execute everything inside of this again.
And that's what we need to use a debounce for.
So what we can do is we can create a variable.
And I'm going to call this variable play button.
Cool down and I'm going to set it equal to false.
And then what we can do is we can go ahead and check before we do anything else, whether or not if
our play button cool down is equal to true.
So if we check if our play button cool down is true, then we're just going to return and not do anything.
Otherwise, if our play button cooldown variable is not equal to true, then we're going to set it equal
to true.
And now that we've done this, if we spam click the button.
All of the code in here is only going to execute once, and then after it's done executing, we can
update the value in our play button.
Cooldown back equal to false to allow us to, um, play or click the button again.
So if I go and play test the game now.
And our two spam.
Click this button.
It's only going to execute the code in there once.
And as you can see, nothing bad happened.
The same thing goes for if I set the game state to be, for example, wave in progress and I go and
play test the game.
If I spam.
Click this button here.
You can see that the text still fades out and nothing bad happened.
If I try to spam, click it again.
As you can see, it's still reset back to its original message and everything was fine.
And that's because we're using this debounce to stop us from executing this function more than once.
So Debounce is are very important when you want to control how many times you want to execute, um,
a particular set of functionality within a function that's connected to an event.
For example, listening to the input for our play button.
So great job making it through this first lecture.
And in the next lecture we're going to script our players camera where we'll have our camera rotate
around the map while the player is in the starting menu.
And then we're going to reset it back to the player's character when they spawn into the map.
So we have a part above our fountain right here, this invisible part.
It's called camera part.
And we're going to set the position of the player's camera equal to this camera part.
And we're going to have it slowly rotate around the map while they have the main menu visible.
And then when they click play it's going to fade the screen to black.
And then it's going to spawn our character into the map.
And then we're going to reset the camera back to our player's character.
So I'll see you there.